/*!
 * Copyright (C) 2020 - All Rights Reserved by
 * @author : ZhaoYanbo
 * @email  : zyb920@hotmail.com
 * @created: 2020-10-31
 * @version: 1.0.0.0
 *
 */

#include "AudioDecoder.h"
#include "OpenALEngine.h"
#include <QtDebug>

extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
}

AudioDecoder::AudioDecoder(QObject *p) :  QThread(p)
{
    av_register_all();

}

AudioDecoder::~AudioDecoder()
{
    this->requestInterruption();
    this->wait();
    if(engine)
    {
        delete engine;
        engine = nullptr;
    }
}

void AudioDecoder::play(const QString &file)
{
    if(m_file == file)
        return;
    m_file = file;

    this->stop();

    this->start();
}

void AudioDecoder::stop()
{
    this->requestInterruption();
    this->wait();
}

void AudioDecoder::run()
{
    QByteArray tmp = m_file.toLocal8Bit();
    const char *url = tmp.constData();

    /* register all the codecs */

    AVFormatContext *fmt_ctx = nullptr;

    //打开文件，解封装
    fmt_ctx = avformat_alloc_context();
    if (avformat_open_input(&fmt_ctx, url, nullptr, nullptr) != 0)
    {
        qDebug() << "open file error";
        return;
    }
    if (avformat_find_stream_info(fmt_ctx, nullptr) < 0)
    {
        qDebug() << "Could't find stream infomation.";
        return;
    }
    //查找解码信息
    int stream_index = -1;
    for (uint i = 0; i < fmt_ctx->nb_streams; ++i)
    {
        if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
            stream_index = static_cast<int>(i);
            break;
        }
    }
    av_dump_format(fmt_ctx, 0, url, false);
    fflush(stdout);

    if (stream_index < 0)
    {
        qDebug() << "Didn't find a audio stream.";
        return;
    }
    qDebug() << "stream index:" << stream_index;

    AVCodec *cod = avcodec_find_decoder(fmt_ctx->streams[stream_index]->codecpar->codec_id);
    if (cod == nullptr)
    {
        qDebug() << "Codec not found.";
        return;
    }
    ///现在根据视频流  打开一个解码器来解码
    AVCodecContext *cod_ctx = avcodec_alloc_context3(cod);
    avcodec_parameters_to_context(cod_ctx, fmt_ctx->streams[stream_index]->codecpar);
    if (avcodec_open2(cod_ctx, cod, nullptr) < 0) {
        qDebug() << "Could not open codec.";
        return;
    }

    //创建packet,用于存储解码前的数据
    AVPacket *packet = av_packet_alloc();
    av_init_packet(packet);

    //设置转码后输出相关参数
    //采样的布局方式
    uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;
    //采样个数
    int out_nb_samples = cod_ctx->frame_size;
    //采样格式
    enum AVSampleFormat  out_sample_fmt = AV_SAMPLE_FMT_S16;
    //采样率
    int out_sample_rate = cod_ctx->sample_rate;//44100
    //通道数
    int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);

    //创建buffer
    int out_buffer_size = av_samples_get_buffer_size(nullptr, out_channels, out_nb_samples, out_sample_fmt, 1);

    qDebug() << "out_nb_samples:" << out_nb_samples << out_buffer_size;
    qDebug() << "Bitrate :" << fmt_ctx->bit_rate;
    qDebug() << "Decoder Name :" << cod_ctx->codec->long_name;
    qDebug() << "Cannels :" << cod_ctx->channels << out_channels;
    qDebug() << "Sample per Second :" << cod_ctx->sample_rate;

    //注意要用av_malloc
    uint8_t *buffer = static_cast<uint8_t *>(av_malloc(static_cast<size_t>(out_buffer_size) ) );

    //创建Frame，用于存储解码后的数据
    AVFrame *frame = av_frame_alloc();
    //设置转码参数
    int64_t in_channel_layout=av_get_default_channel_layout(cod_ctx->channels);

    //打开转码器
    struct SwrContext *convert_ctx = swr_alloc();
    convert_ctx = swr_alloc_set_opts(convert_ctx,
                                     static_cast<int64_t>(out_channel_layout),
                                     out_sample_fmt,
                                     out_sample_rate,
                                     in_channel_layout,
                                     cod_ctx->sample_fmt,
                                     cod_ctx->sample_rate,
                                     0, nullptr);
    //初始化转码器
    swr_init(convert_ctx);

    if(!engine)
    {
        engine = new OpenALEngine;
        engine->setVolume(1.0f);
    }

    int ret = -1;
    //重采样设置选项-----------------------------------------------------------end
    //获取输出的声道个数
    //while循环，每次读取一帧，并转码
    while (!this->isInterruptionRequested() && av_read_frame(fmt_ctx, packet) >= 0)
    {
        if (packet->stream_index == stream_index)
        {
            ret = avcodec_send_packet(cod_ctx, packet);
            av_packet_unref(packet);
            if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
                continue;
            ret = avcodec_receive_frame(cod_ctx, frame);
            if (ret < 0 && ret != AVERROR_EOF)
                continue;

            //转码
            swr_convert(convert_ctx,
                        &buffer,
                        out_buffer_size,
                        (const uint8_t **)frame->data,
                        frame->nb_samples);

            engine->openAudioFromQueue((char*)buffer,
                                       out_buffer_size,
                                       out_sample_rate,
                                       16, 2);

            msleep(engine->m_oneframeduration - 10);
        }
    }
    while(!this->isInterruptionRequested())
    {
        if( !engine->updateQueue() )
            break;
        msleep(10);
    }
    engine->stop();
    delete engine;
    engine = nullptr;

    qDebug() << "end";

    swr_free(&convert_ctx);

    av_free(buffer);
    av_free(frame);
    avcodec_close(cod_ctx);
    avformat_close_input(&fmt_ctx);
}
